﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using GalaSoft.MvvmLight.Messaging;
using Miris.HumanMilkAnalyser.DataModel;
using Miris.HumanMilkAnalyser.Service;
using Miris.HumanMilkAnalyser.Service.Interface;
using Miris.HumanMilkAnalyser.Service.Shared;
using Miris.HumanMilkAnalyser.WPF.Model;
using Miris.HumanMilkAnalyser.WPF.Shared;

namespace Miris.HumanMilkAnalyser.WPF.ViewModel
{
    /// <summary>
    /// This class contains properties that a View can data bind to.
    /// <para>
    /// See http://www.galasoft.ch/mvvm
    /// </para>
    /// </summary>
    public class PatientDataSearchViewModel : MyViewModelBase
    {
        public const string GoToNextPageMessageToken = "PatientDataSearchGoToNextPageMessage";
        public const string DeleteDataConfirmMessageToken = "PatientDataSearchDeleteDataConfirm";
        private IDataService _dataService;
        private IPatientService _patientService;
        private IAnalysisResultService _analysisResultService;

        /// <summary>
        /// Initializes a new instance of the PatientDataSearchViewModel class.
        /// </summary>
        public PatientDataSearchViewModel(IDataService dataService)
            : base(dataService)
        {
            _dataService = dataService;
            _patientService = ServiceFactory.CreatePatientService();
            _analysisResultService = ServiceFactory.CreateAnalysisResultService();
        }

        public override void ResetProperties()
        {
            base.ResetProperties();

            SelectedPatient = null;
            PatientNumber = string.Empty;
            MotherName = string.Empty;
            SampleNumber = string.Empty;
            IsFinishedTest = false;
            StartDate = GeneralUtility.GetCurrentDateTime().AddMonths(-1);
            EndDate = GeneralUtility.GetCurrentDateTime();
            PatientList.Clear();
            AnalysisResultList.Clear();
        }

        /// <summary>
        /// The <see cref="PatientNumber" /> property's name.
        /// </summary>
        public const string PatientNumberPropertyName = "PatientNumber";

        private string _patientNumber = string.Empty;

        /// <summary>
        /// Sets and gets the PatientNumber property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public string PatientNumber
        {
            get
            {
                return _patientNumber;
            }

            set
            {
                if (_patientNumber == value)
                {
                    return;
                }

                RaisePropertyChanging(PatientNumberPropertyName);
                _patientNumber = value;
                RaisePropertyChanged(PatientNumberPropertyName);
            }
        }

        /// <summary>
        /// The <see cref="SampleNumber" /> property's name.
        /// </summary>
        public const string SampleNumberPropertyName = "SampleNumber";

        private string _sampleNumber = string.Empty;

        /// <summary>
        /// Sets and gets the SampleNumber property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public string SampleNumber
        {
            get
            {
                return _sampleNumber;
            }

            set
            {
                if (_sampleNumber == value)
                {
                    return;
                }

                RaisePropertyChanging(SampleNumberPropertyName);
                _sampleNumber = value;
                RaisePropertyChanged(SampleNumberPropertyName);
            }
        }

        /// <summary>
        /// The <see cref="MotherName" /> property's name.
        /// </summary>
        public const string MotherNamePropertyName = "MotherName";

        private string _motherName = string.Empty;

        /// <summary>
        /// Sets and gets the MotherName property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public string MotherName
        {
            get
            {
                return _motherName;
            }

            set
            {
                if (_motherName == value)
                {
                    return;
                }

                RaisePropertyChanging(MotherNamePropertyName);
                _motherName = value;
                RaisePropertyChanged(MotherNamePropertyName);
            }
        }

        /// <summary>
        /// The <see cref="IsFinishedTest" /> property's name.
        /// </summary>
        public const string IsFinishedTestPropertyName = "IsFinishedTest";

        private bool _isFinishedTest = false;

        /// <summary>
        /// Sets and gets the IsFinishedTest property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public bool IsFinishedTest
        {
            get
            {
                return _isFinishedTest;
            }

            set
            {
                if (_isFinishedTest == value)
                {
                    return;
                }

                RaisePropertyChanging(IsFinishedTestPropertyName);
                _isFinishedTest = value;
                RaisePropertyChanged(IsFinishedTestPropertyName);
            }
        }

        /// <summary>
        /// The <see cref="StartDate" /> property's name.
        /// </summary>
        public const string StartDatePropertyName = "StartDate";

        private DateTime _startDate = GeneralUtility.GetCurrentDateTime().AddMonths(-1);

        /// <summary>
        /// Sets and gets the StartDate property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public DateTime StartDate
        {
            get
            {
                return _startDate;
            }

            set
            {
                if (_startDate == value)
                {
                    return;
                }

                RaisePropertyChanging(StartDatePropertyName);
                _startDate = value;
                RaisePropertyChanged(StartDatePropertyName);
            }
        }

        /// <summary>
        /// The <see cref="EndDate" /> property's name.
        /// </summary>
        public const string EndDatePropertyName = "EndDate";

        private DateTime _endDate = GeneralUtility.GetCurrentDateTime();

        /// <summary>
        /// Sets and gets the EndDate property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public DateTime EndDate
        {
            get
            {
                return _endDate;
            }

            set
            {
                if (_endDate == value)
                {
                    return;
                }

                RaisePropertyChanging(EndDatePropertyName);
                _endDate = value;
                RaisePropertyChanged(EndDatePropertyName);
            }
        }

        /// <summary>
        /// The <see cref="SelectedPatient" /> property's name.
        /// </summary>
        public const string SelectedPatientPropertyName = "SelectedPatient";

        private PatientViewModel _selectedPatient = null;

        /// <summary>
        /// Sets and gets the SelectedPatient property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public PatientViewModel SelectedPatient
        {
            get
            {
                return _selectedPatient;
            }

            set
            {
                if (_selectedPatient == value)
                {
                    return;
                }

                RaisePropertyChanging(SelectedPatientPropertyName);
                _selectedPatient = value;
                RaisePropertyChanged(SelectedPatientPropertyName);

                try
                {
                    if (value != null)
                    {
                        LoadAnalysisResult(SelectedPatient.Number);
                    }
                }
                catch (Exception ex)
                {
                    RequestDisplayErrorMessage(ex.Message);
                }
            }
        }

        /// <summary>
        /// The <see cref="PatientList" /> property's name.
        /// </summary>
        public const string PatientListPropertyName = "PatientList";

        private ObservableCollection<PatientViewModel> _patientList = new ObservableCollection<PatientViewModel>();

        /// <summary>
        /// Sets and gets the PatientList property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public ObservableCollection<PatientViewModel> PatientList
        {
            get
            {
                return _patientList;
            }

            set
            {
                if (_patientList == value)
                {
                    return;
                }

                RaisePropertyChanging(PatientListPropertyName);
                _patientList = value;
                RaisePropertyChanged(PatientListPropertyName);
            }
        }

        /// <summary>
        /// The <see cref="AnalysisResultList" /> property's name.
        /// </summary>
        public const string AnalysisResultListPropertyName = "AnalysisResultList";

        private ObservableCollection<AllTestItemsViewModel> _analysisResultList = new ObservableCollection<AllTestItemsViewModel>();

        /// <summary>
        /// Sets and gets the AnalysisResultList property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public ObservableCollection<AllTestItemsViewModel> AnalysisResultList
        {
            get
            {
                return _analysisResultList;
            }

            set
            {
                if (_analysisResultList == value)
                {
                    return;
                }

                RaisePropertyChanging(AnalysisResultListPropertyName);
                _analysisResultList = value;
                RaisePropertyChanged(AnalysisResultListPropertyName);
            }
        }

        private RelayCommand _searchCommand;

        /// <summary>
        /// Gets the SearchCommand.
        /// </summary>
        public RelayCommand SearchCommand
        {
            get
            {
                return _searchCommand ?? (_searchCommand = new RelayCommand(
                    ExecuteSearchCommand,
                    CanExecuteSearchCommand));
            }
        }

        private void ExecuteSearchCommand()
        {
            PatientList.Clear();

            var patient = new Patient
            {
                MotherName = MotherName,
                Number = PatientNumber
            };

            IList<Patient> patients = null;

            if (IsFinishedTest)
            {
                // Search the patient with finished test.
                var selectedStartDate = StartDate.Date;
                var selectedEndDate = new DateTime(EndDate.Year, EndDate.Month, EndDate.Day, 23, 59, 59);

                patients = _patientService.Query(new Patient { Number = PatientNumber, MotherName = MotherName }, 
                    new AnalysisResult { SampleNumber = SampleNumber }, 
                    selectedStartDate, 
                    selectedEndDate);
            }
            else
            {
                // Search the patient with or without the test.
                patients = _patientService.Query(new Patient { Number = PatientNumber, MotherName = MotherName }, null, null, null);
            }

            patients = patients.OrderByDescending(p => p.CreateOn).ToList();

            for (int i = 0; i < patients.Count; i++)
            {
                PatientViewModel patientViewModel = new PatientViewModel(_dataService);
                patientViewModel.SetPatientProperties(patients[i]);
                patientViewModel.SequenceNumber = i + 1;
                PatientList.Add(patientViewModel);
            }

            AnalysisResultList.Clear();
        }

        private bool CanExecuteSearchCommand()
        {
            return StartDate <= EndDate;
        }

        private RelayCommand<IEnumerable<object>> _deletePatientCommand;

        /// <summary>
        /// Gets the DeletePatientCommand.
        /// </summary>
        public RelayCommand<IEnumerable<object>> DeletePatientCommand
        {
            get
            {
                return _deletePatientCommand ?? (_deletePatientCommand = new RelayCommand<IEnumerable<object>>(
                    ExecuteDeletePatientCommand,
                    CanExecuteDeletePatientCommand));
            }
        }

        private void ExecuteDeletePatientCommand(IEnumerable<object> parameter)
        {
            var selectedPatients = parameter.OfType<PatientViewModel>();

            // Show a message to confirm whether user needs to delete the selected data.
            var dialogMessage = new DialogMessage(string.Format("确认删除所选 {0} 条被测者信息以及所有相关的测试数据？该操作将不可恢复！",
                selectedPatients.Count()), dialogResult =>
                {
                    // This is the CallBack of the DialogMessage.
                    if (dialogResult == MessageBoxResult.Yes)
                    {
                        try
                        {
                            DeleteSelectedPatientDataFromDataSource(selectedPatients);
                            RemoveSelectedPatientFromCollection(selectedPatients);
                            AnalysisResultList.Clear();
                        }
                        catch (Exception ex)
                        {
                            RequestDisplayErrorMessage(ex.Message);
                        }
                    }
                })
            {
                Button = MessageBoxButton.YesNo,
                Caption = "删除被测者信息",
                Icon = MessageBoxImage.Question
            };

            Messenger.Default.Send<DialogMessage>(dialogMessage, DeleteDataConfirmMessageToken);
        }

        private bool CanExecuteDeletePatientCommand(IEnumerable<object> parameter)
        {
            if (parameter == null || parameter.Count() == 0)
            {
                return false;
            }
            else
            {
                return true;
            }
        }

        private RelayCommand<IEnumerable<object>> _deleteAnalysisResultCommand;

        /// <summary>
        /// Gets the DeleteAnalysisResultCommand.
        /// </summary>
        public RelayCommand<IEnumerable<object>> DeleteAnalysisResultCommand
        {
            get
            {
                return _deleteAnalysisResultCommand ?? (_deleteAnalysisResultCommand = new RelayCommand<IEnumerable<object>>(
                    ExecuteDeleteAnalysisResultCommand,
                    CanExecuteDeleteAnalysisResultCommand));
            }
        }

        private void ExecuteDeleteAnalysisResultCommand(IEnumerable<object> parameter)
        {
            var selectedAnalysisResults = parameter.OfType<AllTestItemsViewModel>();

            // Show a message to confirm whether user needs to delete the selected data.
            var dialogMessage = new DialogMessage(string.Format("确认删除所选 {0} 条测试数据？该操作将不可恢复！",
                selectedAnalysisResults.Count()), dialogResult =>
                {
                    // This is the CallBack of the DialogMessage.
                    if (dialogResult == MessageBoxResult.Yes)
                    {
                        try
                        {
                            DeleteSelectedTestDataFromDataSource(selectedAnalysisResults);
                            RemoveSelectedAnalysisResultFromCollection(selectedAnalysisResults);
                        }
                        catch (Exception ex)
                        {
                            RequestDisplayErrorMessage(ex.Message);
                        }
                    }
                })
            {
                Button = MessageBoxButton.YesNo,
                Caption = "删除测试数据",
                Icon = MessageBoxImage.Question
            };

            Messenger.Default.Send<DialogMessage>(dialogMessage, DeleteDataConfirmMessageToken);
        }

        private bool CanExecuteDeleteAnalysisResultCommand(IEnumerable<object> parameter)
        {
            if (parameter == null || parameter.Count() == 0)
            {
                return false;
            }
            else
            {
                return true;
            }
        }

        private RelayCommand _goToNextCommand;

        /// <summary>
        /// Gets the GoToNextCommand.
        /// </summary>
        public RelayCommand GoToNextCommand
        {
            get
            {
                return _goToNextCommand
                    ?? (_goToNextCommand = new RelayCommand(
                                          () =>
                                          {
                                              // Send to PatientDataSearchPage, request go to next page.
                                              SelectedPatient.IsNewData = false;
                                              Messenger.Default.Send<PatientViewModel>(SelectedPatient, GoToNextPageMessageToken);
                                          },
                                          () => SelectedPatient == null ? false : true));
            }
        }

        /// <summary>
        /// Delete selected Patient and all related Testing data from data source.
        /// </summary>
        private void DeleteSelectedPatientDataFromDataSource(IEnumerable<PatientViewModel> selectedPatients)
        {
            var removedPatients = selectedPatients.Select(p => new Patient { Number = p.Number });
            _patientService.Delete(removedPatients);
        }

        /// <summary>
        /// Remove selected patient form collection.
        /// </summary>
        private void RemoveSelectedPatientFromCollection(IEnumerable<PatientViewModel> selectedPatients)
        {
            // Remove selected patient.
            PatientList = new ObservableCollection<PatientViewModel>(PatientList.Where(p => !selectedPatients.Contains(p)));
        }

        /// <summary>
        /// Delete selected Testing data from data source.
        /// </summary>
        private void DeleteSelectedTestDataFromDataSource(IEnumerable<AllTestItemsViewModel> selectedAnalysisResults)
        {
            var removedAnalysisResults = selectedAnalysisResults.Select(p => new AnalysisResult { SampleNumber = p.SampleNumber });
            _analysisResultService.Delete(removedAnalysisResults);
        }

        /// <summary>
        /// Remove selected test result form collection.
        /// </summary>
        private void RemoveSelectedAnalysisResultFromCollection(IEnumerable<AllTestItemsViewModel> selectedAnalysisResults)
        {
            // Remove selected test result.
            AnalysisResultList = new ObservableCollection<AllTestItemsViewModel>(AnalysisResultList.Where(p => !selectedAnalysisResults.Contains(p)));
        }

        /// <summary>
        /// Load analysis result data.
        /// </summary>
        /// <param name="patientNumber">The analysis result of this patient with the provided patient number.</param>
        private void LoadAnalysisResult(string patientNumber)
        {
            var selectedStartDate = StartDate.Date;
            var selectedEndDate = new DateTime(EndDate.Year, EndDate.Month, EndDate.Day, 23, 59, 59);
            IList<AnalysisResult> analysisResults;

            if (IsFinishedTest)
            {
                analysisResults = _analysisResultService.Query(p => (SampleNumber.Length == 0 || p.SampleNumber.ToLower().Contains(SampleNumber.ToLower())) &&
                    (p.TestDate >= selectedStartDate && p.TestDate <= selectedEndDate) &&
                    (p.PatientNumber == patientNumber));
            }
            else
            {
                analysisResults = _analysisResultService.Query(p => p.PatientNumber == patientNumber);
            }

            ViewModelHelper.FillAllTestItemsViewModelCollection(analysisResults, AnalysisResultList);
        }
    }
}